---
title: Highlight Overlay
description: Imperative overlay to highlight any DOM element via id or ref.
component: HighlightOverlay
---

## Overview

The `HighlightOverlay` renders a temporary, animated ring on top of a target element. You can target by providing a `highlightedElement` id (string) and/or a map of element refs.

It automatically positions and tracks layout changes, supports optional auto-clear, and can be styled via `className`.

This component is designed to help AI agents guide the user's attention to specific elements on a page (i.e. via a tool call).

## Usage

### Basic

<LiveComponent language="tsx" noInline>
{`
  import { Button, Card, HighlightOverlay } from "@pipecat-ai/voice-ui-kit";

  function Demo() {
    const [highlightedElement, setHighlightedElement] = React.useState<string | null>(null);
    const card1Ref = React.useRef<HTMLDivElement>(null);
    const card2Ref = React.useRef<HTMLDivElement>(null);

    const elementRefs = {
      card1: card1Ref.current,
      card2: card2Ref.current,
    };

    return (
      <div className="flex flex-col gap-4 items-center">
        <ButtonGroup>
          <Button variant="outline" onClick={() => setHighlightedElement("card1")}>Highlight Card 1</Button>
          <Button variant="outline" onClick={() => setHighlightedElement("card2")}>Highlight Card 2</Button>
          <Button variant="outline" onClick={() => setHighlightedElement(null)}>Clear Highlight</Button>
        </ButtonGroup>

        <div className="grid grid-cols-2 gap-6 my-4">
          <Card ref={card1Ref} className="p-6 w-64 flex items-center justify-center">
            <CardHeader>
              <CardTitle className="font-semibold">Card 1</CardTitle>
            </CardHeader>
          </Card>

          <Card ref={card2Ref} className="p-6 w-64 flex items-center justify-center">
            <CardHeader>
              <CardTitle className="font-semibold">Card 2</CardTitle>
            </CardHeader>
          </Card>
        </div>

        <HighlightOverlay
          highlightedElement={highlightedElement}
          elementRefs={elementRefs}
          onHighlightElement={setHighlightedElement}
          className="ring-2 ring-blue-500 ring-offset-2"
          offset={8}
          autoClearDuration={4000}
        />
      </div>
    );
  }

  render(<Demo />)
`}
</LiveComponent>

### Custom styling

<LiveComponent language="tsx" noInline>
{`
  import { Button, Card, HighlightOverlay } from "@pipecat-ai/voice-ui-kit";

  function Demo() {
    const [highlightedElement, setHighlightedElement] = React.useState<string | null>(null);

    return (
      <div className="flex flex-col gap-4 items-center">
        <ButtonGroup>
          <Button variant="outline" onClick={() => setHighlightedElement("card1")}>Highlight Card 1</Button>
          <Button variant="outline" onClick={() => setHighlightedElement("card2")}>Highlight Card 2</Button>
          <Button variant="outline" onClick={() => setHighlightedElement(null)}>Clear Highlight</Button>
        </ButtonGroup>

        <div className="grid grid-cols-2 gap-6 my-4">
          <Card id="card1" className="p-6 w-64 flex items-center justify-center">
            <CardHeader>
              <CardTitle className="font-semibold">Card 1</CardTitle>
            </CardHeader>
          </Card>

          <Card id="card2" className="p-6 w-64 flex items-center justify-center">
            <CardHeader>
              <CardTitle className="font-semibold">Card 2</CardTitle>
            </CardHeader>
          </Card>
        </div>

        <HighlightOverlay
          highlightedElement={highlightedElement}
          onHighlightElement={setHighlightedElement}
          className={highlightedElement === "card1" ? "ring-4 ring-green-500" : "ring-4 ring-red-500"}
          offset={12}
          autoClearDuration={3000}
        />
      </div>
    );
  }

  render(<Demo />)
`}
</LiveComponent>

### No auto-clear

<LiveComponent language="tsx" noInline>
{`
  import { Button, Card, HighlightOverlay } from "@pipecat-ai/voice-ui-kit";

  function Demo() {
    const [highlightedElement, setHighlightedElement] = React.useState<string | null>(null);

    return (
      <div className="flex flex-col gap-4 items-center">
        <ButtonGroup>
          <Button variant="outline" onClick={() => setHighlightedElement("card3")}>Highlight Card 1</Button>
          <Button variant="outline" onClick={() => setHighlightedElement("card4")}>Highlight Card 2</Button>
          <Button variant="outline" onClick={() => setHighlightedElement(null)}>Clear Highlight</Button>
        </ButtonGroup>

        <div className="grid grid-cols-2 gap-6 my-4">
          <Card id="card3" className="p-6 w-64 flex items-center justify-center">
            <CardHeader>
              <CardTitle className="font-semibold">Card 1</CardTitle>
            </CardHeader>
          </Card>

          <Card id="card4" className="p-6 w-64 flex items-center justify-center">
            <CardHeader>
              <CardTitle className="font-semibold">Card 2</CardTitle>
            </CardHeader>
          </Card>
        </div>

        <HighlightOverlay
          highlightedElement={highlightedElement}
          onHighlightElement={setHighlightedElement}
          className="ring-2 ring-purple-500 ring-offset-4"
          offset={6}
          autoClearDuration={0}
        />
      </div>
    );
  }

  render(<Demo />)
`}
</LiveComponent>

---

## Props

<TypeTable
  className="text-sm"
  type={{
    highlightedElement: {
      description: "Id of the element to highlight (with or without leading '#').",
      type: "string | null",
      required: false,
      default: "null",
    },
    elementRefs: {
      description: "Optional map of element ids to HTMLElement refs to avoid DOM lookups.",
      type: "Record<string, HTMLElement | null>",
      required: false,
      default: "{}",
    },
    onHighlightElement: {
      description: "Callback invoked when the overlay auto-clears or when you clear manually.",
      type: "(elementId: string | null) => void",
      required: false,
      default: "undefined",
    },
    className: {
      description: "Classes applied to the overlay ring element.",
      type: "string",
      required: false,
      default: '"ring-4"',
    },
    offset: {
      description: "Extra pixels around the target element bounds.",
      type: "number",
      required: false,
      default: "4",
    },
    autoClearDuration: {
      description: "Milliseconds before auto-clearing. Set to 0 to disable auto-clear.",
      type: "number",
      required: false,
      default: "3000",
    },
  }}
/>



### CSS variables

- **--animate-highlight**: controls the highlight animation shorthand. Default is `highlight-focus 1.2s ease-in-out forwards`.
- **--highlight-final-opacity**: controls the final opacity at the end of the animation. The component sets this automatically based on `autoClearDuration` (0 → `1`, otherwise → `0`), but you can override it.

Override globally (scoped styles use `.voice-ui-kit`):

```css
.voice-ui-kit {
  --animate-highlight: highlight-focus 900ms ease-out forwards;
}
```

Per-instance override:

```tsx
<HighlightOverlay
  highlightedElement="card1"
  style={{ "--highlight-final-opacity": "0.6" }}
/>
```
